﻿using Arch.SOA.ESB.Entity;
using Ctrip.SOA.Comm;
using Ctrip.SOA.Comm.Agent;
using Ctrip.SOA.Comm.Cache;
using Ctrip.SOA.Comm.Entity;
using Ctrip.SOA.Comm.localhost;
using Ctrip.SOA.Comm.Log;
using Ctrip.SOA.Comm.Utility;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml;

namespace EAPWrapper
{
    public interface IESBRequestEventHandler1
    {
        void RequestCompletedEventHandler(object sender, RequestCompletedEventArgs1 e);
    }
    internal class TAPESBRequestEventHandler<TResponse> : IESBRequestEventHandler
    {
        TaskCompletionSource<TResponse> _tcs;
        Func<string, TResponse> _callback;
        public TAPESBRequestEventHandler(TaskCompletionSource<TResponse> tcs,Func<string, TResponse> callback)
        {
            _tcs = tcs;
            _callback = callback;
        }
        public void RequestCompletedEventHandler(object sender, Ctrip.SOA.Comm.localhost.RequestCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                _tcs.TrySetCanceled();
                return;
            }
            else if (e.Error != null)
            {
                _tcs.TrySetException(e.Error);
                return;
            }
            else
            {
                string responseStr = e.Result;
                TResponse response = _callback(responseStr);
                _tcs.TrySetResult(response);
            }
        }
    }
    internal class TAPESBRequestEventHandler : IESBRequestEventHandler1
    {
        TaskCompletionSource<string> _tcs;
        public TAPESBRequestEventHandler(TaskCompletionSource<string> tcs)
        {
            _tcs = tcs;
        }
        public void RequestCompletedEventHandler(object sender, RequestCompletedEventArgs1 e)
        {
            try
            {
                if (e.Cancelled == true)
                {
                    _tcs.TrySetCanceled();
                    return;
                }
                else if (e.Error != null)
                {
                    _tcs.TrySetException(e.Error);
                    return;
                }
                else
                {
                    string responseStr = e.Result;
                    _tcs.TrySetResult(responseStr);
                }
            }
            catch (Exception ex)
            {
                _tcs.TrySetException(ex);
            }
        }
    }
    public class TAPWSAgent
    {
        internal class ClientCacheManager
        {
            private static readonly ClientCacheManager _cacheManage = new ClientCacheManager();
            private static System.Collections.Generic.Dictionary<string, ClientRequestType> _cacheData;
            private Ctrip.SOA.Comm.Cache.ClientCacheLoader _loader;
            private Timer _timer;
            public static ClientCacheManager CurrentInstance
            {
                get
                {
                    return ClientCacheManager._cacheManage;
                }
            }
            private ClientCacheManager()
            {
                ClientCacheManager._cacheData = new System.Collections.Generic.Dictionary<string, ClientRequestType>();
                this._loader = new ClientCacheLoader(AppSetting.ESBUrl2);
                this._timer = new Timer(new TimerCallback((state) =>
                {
                    this.Refresh();
                }), null, AppSetting.ClientUpdateCacheInterval, Timeout.Infinite);
                //this._timer.Elapsed += delegate(object sender, ElapsedEventArgs e)
                //{
                //    this.Refresh();
                //};
                //this._timer.Start();
            }
            public void Refresh()
            {
                try
                {
                    System.Collections.Generic.List<string> keys = null;
                    lock (ClientCacheManager._cacheData)
                    {
                        keys = ClientCacheManager._cacheData.Keys.ToList<string>();
                    }
                    System.Collections.Generic.Dictionary<string, ClientRequestType> dict = new System.Collections.Generic.Dictionary<string, ClientRequestType>();
                    foreach (string key in keys)
                    {
                        string userID = key.Split(new char[]
					{
						'|'
					})[0].Substring(1);
                        string requestType = key.Substring(key.IndexOf('|') + 1);
                        string timestamp = ClientCacheManager._cacheData[key].Timestamp;
                        ClientRequestType rt = this._loader.GetRemoteRequestType(userID, requestType, timestamp);
                        if (rt.IsValid && rt.RequestTypeID == 0L)
                        {
                            rt = ClientCacheManager._cacheData[key];
                        }
                        dict.Add(key, rt);
                    }
                    lock (ClientCacheManager._cacheData)
                    {
                        foreach (string key2 in keys)
                        {
                            ClientCacheManager._cacheData[key2] = dict[key2];
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    CLogWraper.Error("更新缓存失败", ex);
                }
                finally
                {
                    this._timer.Change(AppSetting.ClientUpdateCacheInterval, Timeout.Infinite);
                }
            }
            public ClientRequestType GetRequestType(string userID, string requestType)
            {
                string key = new StringBuilder().Append("$").Append(userID).Append("|").Append(requestType.ToLower()).ToString();
                ClientRequestType result;
                lock (ClientCacheManager._cacheData)
                {
                    if (!ClientCacheManager._cacheData.ContainsKey(key))
                    {
                        ClientRequestType rt = this._loader.GetRemoteRequestType(userID, requestType);
                        ClientCacheManager._cacheData[key] = rt;
                    }
                    result = ClientCacheManager._cacheData[key];
                }
                return result;
            }
        }
        private static string FailResponse(string requestXml, RequestHead head, ResultCode resultcode, string resultmsg)
        {
            Response response = new Response();
            if (head != null)
            {
                response.Header.SetHeaderInfo(head);
            }
            response.Header.SetFailResult(resultcode, "", resultmsg);
            response.Header.RouteStep = (response.Header.RouteStep | 1);
            return XMLSerializer.Serialize(response, typeof(Response));
        }
        /// <summary>
        /// request asynchronously
        /// </summary>
        /// <param name="requestXml">request body in xml format</param>
        /// <returns>response task</returns>
        public async static Task<string> RequestStringAsync(string requestXml)
        {
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>( );
            try
            {
                TAPESBRequestEventHandler requestHandler = new TAPESBRequestEventHandler(tcs);
                RequestHead head;
                
                string url;
                if (ParseRequestHead(requestXml, tcs, out head,out url))
                {
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
  <soap:Body>
    <Request xmlns=""http://tempuri.org/"">
      <requestXML>string</requestXML>
    </Request>
  </soap:Body>
</soap:Envelope>");
                    doc.GetElementsByTagName("requestXML")[0].InnerText = requestXml;
                    string body = doc.InnerXml;
                WebClient client1 = new WebClient();
                client1.Headers["Content-Type"] = "text/xml; charset=utf-8";
                url = "http://localhost:63762/WebService1.asmx";
                var result = await client1.UploadStringTaskAsync(new Uri(url), body);
                //client1.DownloadStringCompleted += new DownloadStringCompletedEventHandler((o, e) =>
                //{
                    
                //tcs.TrySetResult(resp);
                //});
                string resp = result;//.Result;
                doc.LoadXml(resp);
                resp = doc.GetElementsByTagName("RequestResult")[0].InnerText;
                return resp;
                    WSClient1 client=new WSClient1();
                    client.WSUrl = url;
                    client.RequestCompleted+=delegate(object sender, RequestCompletedEventArgs1 e)
                    {
			requestHandler.RequestCompletedEventHandler(sender, e);
		};//requestHandler.RequestCompletedEventHandler;
                    client.RequestAsync(requestXml);
                    //WSAgent.RequestAsync(requestXml, head, 0, requestHandler);
                }
            }
            catch (Exception ex)
            {
                tcs.TrySetException(ex);
            }
            return tcs.Task.Result;
        }

        /// <summary>
        /// 该方法拷贝至框架WSAgent.Request中的部分代码，之所以有这个方法的原因是：框架的异步调用代码(即WSAgent.RequestAsync）中处理ClientRequestType.IsValid的逻辑与同步调用（即WSAgent.Request）的逻辑不一致，导致一旦ClientRequestType.IsValid==false，异步调用会没有任何响应（即回调函数不会触发）。因此添加该方法保持和同步调用的内部实现逻辑一致。
        /// </summary>
        /// <param name="requestXml"></param>
        /// <param name="tcs"></param>
        /// <param name="head"></param>
        /// <returns></returns>
        private static bool ParseRequestHead(string requestXml, TaskCompletionSource<string> tcs, out RequestHead head,out string url)
        {
            head = RequestHead.Parse(requestXml);
            if (!head.AsyncRequest || head.UseMemoryQ)
            {
                ClientRequestType rt = ClientCacheManager.CurrentInstance.GetRequestType(head.UserID, head.RequestType);
                url = rt.WSUrl;
                if (!rt.IsValid)
                {
                    tcs.TrySetResult(FailResponse(requestXml, head, ResultCode.Fail, rt.ErrorMessage));
                    return false;
                }
            }
            else
            {
                url = AppSetting.ESBUrl;
            }
            return true;
        }

        /// <summary>
        ///  该方法拷贝至框架WSAgent.Request中的部分代码，之所以有这个方法的原因是：框架的异步调用代码(即WSAgent.RequestAsync）中处理ClientRequestType.IsValid的逻辑与同步调用（即WSAgent.Request）的逻辑不一致，导致一旦ClientRequestType.IsValid==false，异步调用会没有任何响应（即回调函数不会触发）。因此添加该方法保持和同步调用的内部实现逻辑一致。
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="requestXml"></param>
        /// <param name="tcs"></param>
        /// <param name="head"></param>
        /// <returns></returns>
        private static bool ParseRequestHead<TResponse>(string requestXml, TaskCompletionSource<TResponse> tcs, out RequestHead head)
        {
            head = RequestHead.Parse(requestXml);
            if (!head.AsyncRequest || head.UseMemoryQ)
            {
                ClientRequestType rt = ClientCacheManager.CurrentInstance.GetRequestType(head.UserID, head.RequestType);
                if (!rt.IsValid)
                {
                    string resXML = FailResponse(requestXml, head, ResultCode.Fail, rt.ErrorMessage);

                    tcs.TrySetResult((TResponse)XMLSerializer.DeSerialize(resXML, typeof(TResponse)));
                    return false;
                }
            }
            return true;
        }
        

    }

    public sealed class WSClient1 : IClient, System.IDisposable
    {
        private ESB1 client;
        public event RequestCompletedEventHandler1 RequestCompleted;
        public int Timeout
        {
            get
            {
                return this.client.Timeout / 1000;
            }
            set
            {
                this.client.Timeout = value * 1000;
            }
        }
        public string WSUrl
        {
            get
            {
                return this.client.Url;
            }
            set
            {
                this.client.Url = value;
            }
        }
        public WSClient1()
        {
            this.client = new ESB1();
            if (!string.IsNullOrEmpty(AppSetting.ESBUrl))
            {
                this.client.Url = AppSetting.ESBUrl;
            }
            this.client.RequestCompleted += delegate(object sender, RequestCompletedEventArgs1 e)
            {
                if (this.RequestCompleted != null)
                {
                    this.RequestCompleted(sender, e);
                }
            };
        }
        public WSClient1(string url)
            : this()
        {
            this.client.Url = url;
        }
        public string Request(string request)
        {
            return this.client.Request(request);
        }
        public string Request(string header, string body)
        {
            return string.Empty;
        }
        public void RequestAsync(string request)
        {
            this.client.RequestAsync(request);
        }
        public void Dispose()
        {
            if (this.client != null)
            {
                this.client.Dispose();
            }
        }
    }
    internal sealed class Settings1 : ApplicationSettingsBase
    {
        private static Settings1 defaultInstance = (Settings1)SettingsBase.Synchronized(new Settings1());
        public static Settings1 Default
        {
            get
            {
                return Settings1.defaultInstance;
            }
        }
        [ApplicationScopedSetting, DefaultSettingValue("http://localhost/NewESB/Ctrip.SOA.ESB.asmx"), SpecialSetting(SpecialSetting.WebServiceUrl), System.Diagnostics.DebuggerNonUserCode]
        public string Ctrip_SOA_Comm_localhost_ESB
        {
            get
            {
                return (string)this["Ctrip_SOA_Comm_localhost_ESB"];
            }
        }
    }

    [GeneratedCode("System.Web.Services", "4.0.30319.1"), DesignerCategory("code"), WebServiceBinding(Name = "ESBSoap", Namespace = "http://tempuri.org/")]
	
    public class ESB1 : SoapHttpClientProtocol
    {
        private System.Threading.SendOrPostCallback RequestOperationCompleted;
        private System.Threading.SendOrPostCallback CommRequestOperationCompleted;
        private bool useDefaultCredentialsSetExplicitly;
        public event RequestCompletedEventHandler1 RequestCompleted;
        public event CommRequestCompletedEventHandler1 CommRequestCompleted;
        public new string Url
        {
            get
            {
                return base.Url;
            }
            set
            {
                if (this.IsLocalFileSystemWebService(base.Url) && !this.useDefaultCredentialsSetExplicitly && !this.IsLocalFileSystemWebService(value))
                {
                    base.UseDefaultCredentials = false;
                }
                base.Url = value;
            }
        }
        public new bool UseDefaultCredentials
        {
            get
            {
                return base.UseDefaultCredentials;
            }
            set
            {
                base.UseDefaultCredentials = value;
                this.useDefaultCredentialsSetExplicitly = true;
            }
        }
        public ESB1()
        {
            this.Url = Settings1.Default.Ctrip_SOA_Comm_localhost_ESB;
            if (this.IsLocalFileSystemWebService(this.Url))
            {
                this.UseDefaultCredentials = true;
                this.useDefaultCredentialsSetExplicitly = false;
                return;
            }
            this.useDefaultCredentialsSetExplicitly = true;
        }
        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest request = base.GetWebRequest(uri);
            if (HttpContext.Current != null)
            {
                string requestType = (HttpContext.Current.Items["Tracing-RequestType"] ?? "").ToString();
                string spanId = (HttpContext.Current.Items["Tracing-SpanId"] ?? "").ToString();
                string traceId = (HttpContext.Current.Items["__Clogging_CorrelationId__"] ?? "").ToString();
                if (!string.IsNullOrEmpty(spanId))
                {
                    request.Headers.Add("Tracing-SpanId", spanId);
                }
                if (!string.IsNullOrEmpty(traceId))
                {
                    request.Headers.Add("Tracing-TraceId", traceId);
                }
                if (!string.IsNullOrEmpty(requestType))
                {
                    request.Headers.Add("Tracing-RequestType", requestType);
                }
                if (!string.IsNullOrEmpty(AppSetting.Environment))
                {
                    request.Headers.Add("Environment", AppSetting.Environment);
                }
                request.Headers.Add("ClientAppId", ConfigurationManager.AppSettings["AppID"]);
            }
            return request;
        }
        [SoapDocumentMethod("http://tempuri.org/Request", RequestNamespace = "http://tempuri.org/", ResponseNamespace = "http://tempuri.org/", Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
        public string Request(string requestXML)
        {
            object[] results = base.Invoke("Request", new object[]
			{
				requestXML
			});
            return (string)results[0];
        }
        public void RequestAsync(string requestXML)
        {
            this.RequestAsync(requestXML, null);
        }
        public void RequestAsync(string requestXML, object userState)
        {
            if (this.RequestOperationCompleted == null)
            {
                this.RequestOperationCompleted = new System.Threading.SendOrPostCallback(this.OnRequestOperationCompleted);
            }
            base.InvokeAsync("Request", new object[]
			{
				requestXML
			}, this.RequestOperationCompleted, userState);
        }
        private void OnRequestOperationCompleted(object arg)
        {
            if (this.RequestCompleted != null)
            {
                InvokeCompletedEventArgs invokeArgs = (InvokeCompletedEventArgs)arg;
                this.RequestCompleted(this, new RequestCompletedEventArgs1(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
            }
        }
        [SoapDocumentMethod("http://tempuri.org/CommRequest", RequestNamespace = "http://tempuri.org/", ResponseNamespace = "http://tempuri.org/", Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
        public string CommRequest(string requestXML)
        {
            object[] results = base.Invoke("CommRequest", new object[]
			{
				requestXML
			});
            return (string)results[0];
        }
        public void CommRequestAsync(string requestXML)
        {
            this.CommRequestAsync(requestXML, null);
        }
        public void CommRequestAsync(string requestXML, object userState)
        {
            if (this.CommRequestOperationCompleted == null)
            {
                this.CommRequestOperationCompleted = new System.Threading.SendOrPostCallback(this.OnCommRequestOperationCompleted);
            }
            base.InvokeAsync("CommRequest", new object[]
			{
				requestXML
			}, this.CommRequestOperationCompleted, userState);
        }
        private void OnCommRequestOperationCompleted(object arg)
        {
            if (this.CommRequestCompleted != null)
            {
                InvokeCompletedEventArgs invokeArgs = (InvokeCompletedEventArgs)arg;
                this.CommRequestCompleted(this, new CommRequestCompletedEventArgs1(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
            }
        }
        public new void CancelAsync(object userState)
        {
            base.CancelAsync(userState);
        }
        private bool IsLocalFileSystemWebService(string url)
        {
            if (url == null || url == string.Empty)
            {
                return false;
            }
            Uri wsUri = new Uri(url);
            return wsUri.Port >= 1024 && string.Compare(wsUri.Host, "localHost", System.StringComparison.OrdinalIgnoreCase) == 0;
        }
    }
    public class RequestCompletedEventArgs1 : AsyncCompletedEventArgs
    {
        private object[] results;
        public string Result
        {
            get
            {
                base.RaiseExceptionIfNecessary();
                return (string)this.results[0];
            }
        }
        internal RequestCompletedEventArgs1(object[] results, System.Exception exception, bool cancelled, object userState)
            : base(exception, cancelled, userState)
        {
            this.results = results;
        }
    }
    public class CommRequestCompletedEventArgs1 : AsyncCompletedEventArgs
    {
        private object[] results;
        public string Result
        {
            get
            {
                base.RaiseExceptionIfNecessary();
                return (string)this.results[0];
            }
        }
        internal CommRequestCompletedEventArgs1(object[] results, System.Exception exception, bool cancelled, object userState)
            : base(exception, cancelled, userState)
        {
            this.results = results;
        }
    }
    public delegate void CommRequestCompletedEventHandler1(object sender, CommRequestCompletedEventArgs1 e);
    public delegate void RequestCompletedEventHandler1(object sender, RequestCompletedEventArgs1 e);

}
